//********
/*!

   \file

   © 2011 Mark W. Gabby II
*/
#include "../../Src/OS/OS.hpp"

#include <fstream>
#include <map>

// Windows XP
#define WINVER 0x0501
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Shlobj.h> // For font path function
#include <mmsystem.h> // For controller support
#include <gl/gl.h>
#include <gl/wglext.h>
#include <gl/glext.h>

#include "glCP.hpp"

#include "AvionWin.hpp"

#include "ocWindowsWindow.hpp"
#include "WindowsSpecific.hpp"

typedef uint32_t tSystemScreenModeIndex;
typedef uint8_t tSystemMonitorIndex;

extern HINSTANCE gInstanceHandle;

typedef std::pair<tValidScreenModeIndex, tValidMonitorIndex> tModeAndMonitor;

struct OSDataStruct
{
   // Screen Mode Information
   std::map<tModeAndMonitor, ScreenMode> ModeAndMonitorToScreenMode;
   std::map<tModeAndMonitor, tSystemScreenModeIndex> ModeAndMonitorToSystemScreenModeIndex;
   std::map<tValidMonitorIndex, tValidScreenModeIndex> ModesPerMonitor;

   // Pixel Format Information
   std::vector<PixelFormat> SupportedPixelFormats;
   std::map<tValidPixelFormatIndex, tSystemPixelFormatIndex> SupportedPixelFormatsToSystemPixelFormats;

   // Monitor Information
   std::vector<MonitorInfo> MonitorInfos;
   std::map<tValidMonitorIndex, tSystemMonitorIndex> MonitorIndexToSystemMonitorIndex;

   // Controller information
   std::vector<ControllerInfo> ControllerInfos;

   // Windows and active OpenGL context tracking information
   tWindowID WindowWithActiveContextID;
   std::map<tWindowID, ocWindowsWindow *> WindowIDsToWindows;

   /*!
      \brief
         Returns true if this a valid pixel format, false otherwise.
      A valid format supports enough features, for example, the right number of bits per pixel,
      support for the depth buffer, and double buffering.

      \param FormatIndex
         System format index of pixel format to check.
      \param Format
         Engine-localized version of the format created from system version.
      \param PreviousContext
         If this was the current context when the function starts, it will be restored as such when
         the function ends.

      \return
         True if the format is valid, false otherwise.
   */
   bool IsValidPixelFormat( const tSystemPixelFormatIndex FormatIndex, const PixelFormat & Format,
                            ScopedOpenGLContext & PreviousContext )
   {
      // Get through the gauntlet. If you do, you're valid.
      if ( Format.acceleration != kFull )
      {
         return false;
      }

      // We want 16 or 32 bits per pixel.
      if ( ( Format.bitsPerPixel != 32 ) && ( Format.bitsPerPixel != 24 ) && ( Format.bitsPerPixel != 16 ) )
      {
         return false;
      }

      if ( ( Format.bitPerColor[kA] > 1 ) == false )
      {
         return false;
      }

      // We need SOME form of depth buffer.
      if ( Format.depthBufferBits == 0 )
      {
         return false;
      }

      // Double-buffering is a must.
      if ( Format.doubleBuffered == false )
      {
         return false;
      }

      // If this format can't draw to a window, then we can't use it.
      if ( Format.glWindowSupported == false )
      {
         return false;
      }

      // We don't want a color indexed mode.
      if ( Format.RGBA == false )
      {
         return false;
      }

      const bool PreviousWasCurrent = PreviousContext.IsCurrent();

      // Check it for supported features
      ScopedOpenGLContext * ScopedContext = new ScopedOpenGLContext();
      ScopedContext->Init( FormatIndex );
      CrossPlatformOpenGL::Initialize();
      const bool EnoughFeatures = CrossPlatformOpenGL::SufficientFeaturesSupported();
      delete ScopedContext;
      ScopedContext = NULL;

      if ( PreviousWasCurrent )
      {
         PreviousContext.MakeCurrent();
      }
      return EnoughFeatures;
   }
};


OperatingSystem::OperatingSystem()
{
   osData = new OSDataStruct;
   osData->WindowWithActiveContextID = kNull8;
}
OperatingSystem::~OperatingSystem()
{
   delete osData;
   osData = NULL;
}

void OperatingSystem::RemoveComponentFromSpecificLists( const Component * ToRemove )
{
   typename std::map<tWindowID, ocWindowsWindow *>::iterator CurComponentIt;

   for ( CurComponentIt = osData->WindowIDsToWindows.begin();
         CurComponentIt != osData->WindowIDsToWindows.end(); ++CurComponentIt )
   {
      Component * CurComponent = ( *CurComponentIt ).second;

      if ( CurComponent == ToRemove )
      {
         osData->WindowIDsToWindows.erase( CurComponentIt );
         return;
      }
   }

}

ocWindow * OperatingSystem::CreateWindowComp( Entity * Owner )
{
   ocWindowsWindow * NewWindowsWindow = new ocWindowsWindow( Owner );

   // Keep the map up-to-date
   const tWindowID WindowID = NewWindowsWindow->GetID();
   osData->WindowIDsToWindows[WindowID] = NewWindowsWindow;

   return NewWindowsWindow;
}

bool OperatingSystem::InitializeMonitorInfosList()
{
   // Get the structure that will be used to call the function.
   DISPLAY_DEVICE device = { 0 };
   // Set it up so it's ready for use.
   device.cb = sizeof( DISPLAY_DEVICE );

   // For each monitor ...
   uint8_t deviceIndex = 0;
   uint32_t MaxDeviceString;
   CONSTANTS.Get( MaxDeviceString, "MaxDeviceString" );

   while ( EnumDisplayDevices( NULL, deviceIndex, &device, 0 ) )
   {
      MonitorInfo CurMonitor;

      CurMonitor.name.resize( MaxDeviceString );
      // Copy the string for the monitor name to the current monitor data structure.
      for ( uint32_t i = 0; i < MaxDeviceString; ++i )
      {
         CurMonitor.name[i] = device.DeviceName[i];

         if ( !CurMonitor.name[i] )
         {
            break;
         }
      }

      CurMonitor.string.resize( MaxDeviceString );
      // Copy the string from the device to the ret value.
      for( uint32_t i = 0; i < MaxDeviceString; ++i )
      {
        CurMonitor.string[i] = device.DeviceString[i];

        if( !CurMonitor.string[i] )
        {
            break;
        }
      }

      // Check to see if this is the primary device. If so, set the
      // 'primary' flag to true.
      CurMonitor.valid = false;
      CurMonitor.primary = false;
      if ( device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE )
      {
         CurMonitor.valid = true;
         CurMonitor.primary = true;
      }
      // Check to see if this monitor is active and not mirroring another monitor. If so, set the
      // 'valid' flag to true.
      else if ( ( device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP ) &&
           !( device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER ) )
      {
         CurMonitor.valid = true;
      }

      if ( CurMonitor.valid )
      {
         const int CurMonitorIndex = osData->MonitorInfos.size();
         osData->MonitorInfos.push_back( CurMonitor );
         osData->MonitorIndexToSystemMonitorIndex[CurMonitorIndex] = deviceIndex;
      }

      ++deviceIndex;
   }

   // No monitors found. That's not good.
   if ( osData->MonitorInfos.size() > 0 )
   {
      return true;
   }
   return false;
}

bool OperatingSystem::InitializeValidScreenModesList()
{
   // For each monitor
   const int MonitorCount = GetMonitorCount();
   for ( tValidMonitorIndex MonitorIndex = 0; MonitorIndex < MonitorCount; ++MonitorIndex )
   {
      tValidScreenModeIndex ValidScreenModeCount = 0;

      // Get its system index
      tSystemMonitorIndex SystemMonitorIndex = osData->MonitorIndexToSystemMonitorIndex[MonitorIndex];
      // Using its index, get its device name
      // Get the structure that will be used to call the function.
      DISPLAY_DEVICE Device = { 0 };
      // Set it up so it's ready for use.
      Device.cb = sizeof( DISPLAY_DEVICE );
      EnumDisplayDevices( NULL, SystemMonitorIndex, &Device, 0 );

      //
      DEVMODE DeviceMode;
      DeviceMode.dmSize = sizeof( DEVMODE );
      DeviceMode.dmDriverExtra = 0;

      // For each mode of this monitor:
      tSystemScreenModeIndex SystemScreenModeIndex = 0;
      while ( EnumDisplaySettings( Device.DeviceName, SystemScreenModeIndex, &DeviceMode ) )
      {
         // Initialize the data structure
         ScreenMode Mode;

         Mode.bitsPerPixel = static_cast<uint8_t>( DeviceMode.dmBitsPerPel );
         Mode.dimensions[kHeight] = DeviceMode.dmPelsHeight;
         Mode.dimensions[kWidth] = DeviceMode.dmPelsWidth;
         Mode.refreshRate = static_cast<uint8_t>( DeviceMode.dmDisplayFrequency );
         // Verify it's valid
         // If so:
         if ( IsValidScreenMode( Mode ) )
         {
            // Add it to the map
            tModeAndMonitor ModeAndMonitor;
            ModeAndMonitor.first = ValidScreenModeCount;
            ModeAndMonitor.second = MonitorIndex;
            osData->ModeAndMonitorToSystemScreenModeIndex[ModeAndMonitor] = SystemScreenModeIndex;
            osData->ModeAndMonitorToScreenMode[ModeAndMonitor] = Mode;
            ++ValidScreenModeCount;
         }
         ++SystemScreenModeIndex;
      }
      osData->ModesPerMonitor[MonitorIndex] = ValidScreenModeCount;
   }

   return true;
}

bool OperatingSystem::InitializeValidPixelFormatsList()
{
   // In order to access pixel formats, we need to have an OpenGL rendering
   // context.
   ScopedOpenGLContext ScopedContext;
   ScopedContext.Init();
   HDC DeviceContextHandle = ScopedContext.GetDC();

   const uint32_t FormatCount = static_cast<uint32_t>( DescribePixelFormat( DeviceContextHandle, 1, 0, NULL ) );

   // For the wgl function:
   static PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB = NULL;

   if ( wglGetPixelFormatAttribivARB == NULL )
   {
      wglGetPixelFormatAttribivARB = ( PFNWGLGETPIXELFORMATATTRIBIVARBPROC )
                                     wglGetProcAddress( "wglGetPixelFormatAttribivARB" );
   }

   // If we can use the extension, then go for it:
   if ( wglGetPixelFormatAttribivARB != NULL )
   {
      for ( tSystemPixelFormatIndex i = 0; i < FormatCount; ++i )
      {
         PixelFormat pf;

         int attributeInfo[15];

         // These are the attributes that concern us:
         int attributes [15] = { WGL_SUPPORT_OPENGL_ARB, // 0
                                 WGL_ACCELERATION_ARB,   // 1
                                 WGL_DRAW_TO_WINDOW_ARB, // 2
                                 WGL_DOUBLE_BUFFER_ARB,  // 3
                                 WGL_PIXEL_TYPE_ARB,     // 4
                                 WGL_DEPTH_BITS_ARB,     // 5
                                 WGL_STENCIL_BITS_ARB,   // 6
                                 WGL_SAMPLE_BUFFERS_ARB, // 7
                                 WGL_SAMPLES_ARB,        // 8
                                 WGL_ACCUM_BITS_ARB,     // 9
                                 WGL_RED_BITS_ARB,       // 10
                                 WGL_GREEN_BITS_ARB,     // 11
                                 WGL_BLUE_BITS_ARB,      // 12
                                 WGL_ALPHA_BITS_ARB,     // 13
                                 WGL_COLOR_BITS_ARB      // 14
                               };

         // Get the pixel format information.
         wglGetPixelFormatAttribivARB( DeviceContextHandle, i, 0, 16, attributes, attributeInfo );

         pf.bitPerColor[kR] = static_cast<uint8_t>( attributeInfo[10] );
         pf.bitPerColor[kG] = static_cast<uint8_t>( attributeInfo[11] );
         pf.bitPerColor[kB] = static_cast<uint8_t>( attributeInfo[12] );
         pf.bitPerColor[kA] = static_cast<uint8_t>( attributeInfo[13] );

         pf.bitsPerPixel = static_cast<uint8_t>( attributeInfo[14] );
         pf.accumBufferBits = static_cast<uint8_t>( attributeInfo[9] );
         pf.depthBufferBits = static_cast<uint8_t>( attributeInfo[5] );

         pf.stencilBits = static_cast<uint8_t>( attributeInfo[6] );

         if ( attributeInfo[3] == 1 )
         {
            pf.doubleBuffered = true;
         }

         if ( attributeInfo[0] == 1 && attributeInfo[2] == 1 )
         {
            pf.glWindowSupported = true;
         }

         switch ( attributeInfo[1] )
         {
            case WGL_FULL_ACCELERATION_ARB:
               pf.acceleration = kFull;
               break;

            case WGL_GENERIC_ACCELERATION_ARB:
               pf.acceleration = kPartial;
               break;

            case WGL_NO_ACCELERATION_ARB:
               pf.acceleration = kNone;
               break;

            default:
               break;
         }

         if ( attributeInfo[4] == WGL_TYPE_RGBA_ARB )
         {
            pf.RGBA = true;
         }

         if ( attributeInfo[7] == 1 )              // Multisampled
         {
            pf.multisampleMode = static_cast<uint8_t>( attributeInfo[8] );
         }

         if ( osData->IsValidPixelFormat( i, pf, ScopedContext ) )
         {
            const tValidPixelFormatIndex PFIndex = osData->SupportedPixelFormats.size();
            osData->SupportedPixelFormats.push_back( pf );
            osData->SupportedPixelFormatsToSystemPixelFormats[PFIndex] = i;
         }
      }
   }
   else
   {
      for ( uint32_t i = 0; i < FormatCount; ++i )
      {
         PIXELFORMATDESCRIPTOR pfd;
         pfd.nVersion = 1;
         pfd.nSize = sizeof( pfd );

         DescribePixelFormat( DeviceContextHandle, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfd );

         // If the generic format flag is clear, then this format is fully accelerated.
         PixelFormat pf;
         if ( !( pfd.dwFlags & PFD_GENERIC_FORMAT ) )
         {
            pf.acceleration = kFull;
         }
         else
         {
            // If this flag is set, then we have a bare minimum of features.
            if ( pfd.dwFlags & PFD_GENERIC_ACCELERATED )
            {
               pf.acceleration = kPartial;
            }
            // Otherwise, we have nothing.
            else
            {
               pf.acceleration = kNone;
            }
         }

         pf.accumBufferBits = pfd.cAccumBits;
         pf.bitPerColor[kR] = pfd.cRedBits;
         pf.bitPerColor[kG] = pfd.cGreenBits;
         pf.bitPerColor[kB] = pfd.cBlueBits;
         pf.bitPerColor[kA] = pfd.cAlphaBits;
         pf.bitsPerPixel = pfd.cColorBits;
         pf.depthBufferBits = pfd.cDepthBits;

         if ( pfd.dwFlags & PFD_DOUBLEBUFFER )
         {
            pf.doubleBuffered = true;
         }

         pf.multisampleMode = 0;
         pf.stencilBits = pfd.cStencilBits;

         if ( ( pfd.dwFlags & PFD_SUPPORT_OPENGL ) && ( pfd.dwFlags & PFD_DRAW_TO_WINDOW ) )
         {
            pf.glWindowSupported = true;
         }

         if ( pfd.iPixelType == PFD_TYPE_RGBA )
         {
            pf.RGBA = true;
         }

         if ( osData->IsValidPixelFormat( i, pf, ScopedContext ) )
         {
            const tValidPixelFormatIndex PFIndex = osData->SupportedPixelFormats.size();
            osData->SupportedPixelFormats.push_back( pf );
            osData->SupportedPixelFormatsToSystemPixelFormats[PFIndex] = i;
         }
      }
   }

   ScopedContext.ReleaseDC( DeviceContextHandle );

   if ( osData->SupportedPixelFormats.size() > 0 )
   {
      return true;
   }
   return false;
}

tValidMonitorIndex OperatingSystem::GetMonitorCount()
{
   return osData->MonitorInfos.size();
}

MonitorInfo OperatingSystem::GetValidMonitorInfo( tValidMonitorIndex MonitorIndex )
{
   return osData->MonitorInfos[MonitorIndex];
}

bool OperatingSystem::SetScreenMode( tValidScreenModeIndex ScreenModeIndex, tValidMonitorIndex MonitorIndex )
{
   // Get the system screen mode index for this monitor and screen mode
   tModeAndMonitor ModeAndMonitor;
   ModeAndMonitor.first = ScreenModeIndex;
   ModeAndMonitor.second = MonitorIndex;
   tSystemScreenModeIndex SystemScreenModeIndex = osData->ModeAndMonitorToSystemScreenModeIndex[ModeAndMonitor];

   tSystemMonitorIndex SystemMonitorIndex = osData->MonitorIndexToSystemMonitorIndex[MonitorIndex];

   // Get the monitor device information
   DISPLAY_DEVICE Device = { 0 };
   // Set it up so it's ready for use.
   Device.cb = sizeof( DISPLAY_DEVICE );
   EnumDisplayDevices( NULL, SystemMonitorIndex, &Device, 0 );

   // Get the devicemode using the device name
   DEVMODE DeviceMode;
   DeviceMode.dmSize = sizeof( DEVMODE );
   DeviceMode.dmDriverExtra = 0;
   EnumDisplaySettings( Device.DeviceName, SystemScreenModeIndex, &DeviceMode );

   // Change the screen resolution (temporarily, will revert after the application exits)
   return ChangeDisplaySettings( &DeviceMode, CDS_FULLSCREEN ) == DISP_CHANGE_SUCCESSFUL;
}

tValidScreenModeIndex OperatingSystem::GetValidScreenModesCount( tValidMonitorIndex ValidMonitorIndex )
{
   return osData->ModesPerMonitor[ValidMonitorIndex];
}

ScreenMode OperatingSystem::GetValidScreenMode( tValidScreenModeIndex ModeIndex,
                                                tValidMonitorIndex MonitorIndex )
{
   tModeAndMonitor ModeAndMonitor;
   ModeAndMonitor.first = ModeIndex;
   ModeAndMonitor.second = MonitorIndex;
   return osData->ModeAndMonitorToScreenMode[ModeAndMonitor];
}

tSystemPixelFormatIndex OperatingSystem::GetSystemPixelFormatIndex( tValidPixelFormatIndex FormatIndex )
{
   return osData->SupportedPixelFormatsToSystemPixelFormats[FormatIndex];
}

tValidPixelFormatIndex OperatingSystem::GetValidPixelFormatsCount()
{
   return osData->SupportedPixelFormats.size();
}

PixelFormat OperatingSystem::GetValidPixelFormat( tValidPixelFormatIndex FormatIndex )
{
   return osData->SupportedPixelFormats[FormatIndex];
}

void OperatingSystem::InitializeControllersList()
{
   const tControllerIndex MaxNumControllers = joyGetNumDevs();
   JOYCAPS CurJoyCaps;
   const uint32_t JoyCapsSize = sizeof( CurJoyCaps );
   for ( tControllerIndex i = 0; i < MaxNumControllers; ++i )
   {
     JOYINFO JoyInfo;

     if ( joyGetPos( i, &JoyInfo ) == JOYERR_NOERROR )
     {
        joyGetDevCaps( i, &CurJoyCaps, JoyCapsSize );

        ControllerInfo Info;
        Info.NumButtons = CurJoyCaps.wNumButtons;
        Info.NumAxes = CurJoyCaps.wNumAxes;
        Info.Hat = CurJoyCaps.wCaps & JOYCAPS_HASPOV ? true : false;

        osData->ControllerInfos.push_back( Info );
     }
   }
}

ControllerInfo OperatingSystem::GetControllerInfo( const tControllerIndex ControllerIndex )
{
   return osData->ControllerInfos[ControllerIndex];
}

tControllerIndex OperatingSystem::GetControllerCount()
{
   return osData->ControllerInfos.size();
}

ocWindowsWindow * GetActiveContextWindow( OSDataStruct * OSData )
{
   if ( OSData->WindowWithActiveContextID != kNull8 )
   {
      return OSData->WindowIDsToWindows[OSData->WindowWithActiveContextID];
   }
   return NULL;
}

bool OperatingSystem::CaptureController( const tControllerIndex ControllerIndex )
{
   ocWindowsWindow * WindowWithActiveContext = GetActiveContextWindow( osData );
   if ( WindowWithActiveContext != NULL )
   {
      return WindowWithActiveContext->CaptureController( ControllerIndex );
   }
   return false;
}

bool OperatingSystem::ReleaseController()
{
   ocWindowsWindow * WindowWithActiveContext = GetActiveContextWindow( osData );
   if ( WindowWithActiveContext != NULL )
   {
      return WindowWithActiveContext->ReleaseController();
   }
   return false;
}

tControllerIndex OperatingSystem::CapturedControllerIndex()
{
   ocWindowsWindow * WindowWithActiveContext = GetActiveContextWindow( osData );
   if ( WindowWithActiveContext != NULL )
   {
      return WindowWithActiveContext->GetControllerIndex();
   }
   return kNull8;
}

void OperatingSystem::SwapBuffers()
{
   ocWindowsWindow * WindowWithActiveContext = GetActiveContextWindow( osData );
   if ( WindowWithActiveContext != NULL )
   {
      WindowWithActiveContext->SwapBuffers();
   }
}

void OperatingSystem::SetActiveContext( tWindowID WindowID )
{
   // Look for the window we're supposed to set the context of
   ocWindowsWindow * NewActiveContextWindow = NULL;
   std::map<tWindowID, ocWindowsWindow *>::iterator it;
   it = osData->WindowIDsToWindows.find( WindowID );
   if ( it != osData->WindowIDsToWindows.end() )
   {
      NewActiveContextWindow = (*it).second;
   }

   // If we found it:
   if ( NewActiveContextWindow != NULL )
   {
      // If there's an existing window with an active context, then deactivate its context and then
      // enable the new one
      ocWindowsWindow * WindowWithActiveContext = GetActiveContextWindow( osData );
      if ( WindowWithActiveContext != NULL )
      {
         std::map<tWindowID, ocWindowsWindow *>::iterator it;
         it = osData->WindowIDsToWindows.find( WindowID );
         if ( it != osData->WindowIDsToWindows.end() )
         {
            WindowWithActiveContext->DeactivateContext();
            NewActiveContextWindow->ActivateContext();
            osData->WindowWithActiveContextID = WindowID;
         }
      }
      else
      {
         // Active this new window's context
         NewActiveContextWindow->ActivateContext();
         osData->WindowWithActiveContextID = WindowID;
      }
   }
}

void OperatingSystem::WriteMonitors( std::ofstream & Out )
{
   MonitorInfo monitor = { 0 };

   uint8_t monitorCount = OS.GetMonitorCount();

   for ( uint8_t i = 0; i < monitorCount; ++i )
   {
      monitor = GetValidMonitorInfo( i );
      Out << monitor.name << std::endl << monitor.string << std::endl;
   }

   Out << std::endl;
}

void OperatingSystem::WriteDisplayFormats( std::ofstream & Out )
{
   // Get the number of screen formats.
   tValidScreenModeIndex ScreenModeCount = GetValidScreenModesCount( 0 );

   // Go through all the formats and output information about them.
   for ( tValidScreenModeIndex i = 0; i < ScreenModeCount; ++i )
   {
      ScreenMode s;
      s = GetValidScreenMode( i, 0 );

      Out << s.dimensions[kWidth] << "x" << s.dimensions[kHeight] << "x" <<
          static_cast<unsigned int>( s.bitsPerPixel ) <<
          "\t\t" << s.refreshRate << "Hz" << std::endl;
   }

   Out << std::endl;
}

void OperatingSystem::WritePixelFormats( std::ofstream & Out )
{
   uint32_t pixelFormatCount = osData->SupportedPixelFormats.size();

   // Pixel formats are 1-indexed .
   for ( uint32_t i = 1; i <= pixelFormatCount ; ++i )
   {
      PixelFormat & pf = osData->SupportedPixelFormats[i];

      switch ( pf.acceleration )
      {
         case kNone:
            Out << "None";
            break;

         case kPartial:
            Out << "Partial";
            break;

         case kFull:
            Out << "Full";
            break;

         default:
            break;
      }

      Out << "\t" << ( unsigned int )pf.bitsPerPixel << "bpp";

      Out << "\t";

      if ( pf.accumBufferBits < 10 )
      {
         Out << " ";
      }

      Out << ( unsigned int )pf.accumBufferBits << " Accum ";

      Out << "\t" << ( unsigned int )pf.depthBufferBits << " Depth";

      Out << "\t";

      if ( pf.depthBufferBits < 10 )
      {
         Out << "\t";
      }

      if ( !pf.doubleBuffered )
      {
         Out << "No ";
      }

      Out << "Double Buffer";

      if ( pf.doubleBuffered )
      {
         Out << "\t";
      }

      Out << "\t" << ( unsigned int )pf.multisampleMode << " MultiSamples";

      if ( pf.RGBA )
      {
         Out << "\t" << "RGBA";
      }
      else
      {
         Out << "\t" << "Index";
      }

      if ( pf.stencilBits )
      {
         Out << "\t" << ( unsigned int )pf.stencilBits << " Stencil";
      }
      else
      {
         Out << "\t" << 0 << " Stencil";
      }

      if ( pf.glWindowSupported )
      {
         Out << "\t Supported" << std::endl;
      }
      else
      {
         Out << "\t Not Supported" << std::endl;
      }
   }

   Out << std::endl;
}

// Misc display functions
float OperatingSystem::GetDesktopGamma()
{
   return 1.0f;
}

std::string OperatingSystem::GetFontDirectory()
{
   // Create the font list
   uint32_t MaxFilePath;
   CONSTANTS.Get( MaxFilePath, "MaxFilePath" );
   char * Path = new char[MaxFilePath];
   memset( Path, 0, MaxFilePath );

   SHGetFolderPath( NULL, CSIDL_FONTS, NULL, 0, Path );

   std::string FontDirectory = Path;

   delete[] Path;

   return FontDirectory;
}

bool OperatingSystem::IsKeyboardKeyDown( const eKeyboardKey Which )
{
   switch ( Which )
   {
   case KK_Escape:
   {
      return (bool)(GetAsyncKeyState( VK_ESCAPE ) & 0x8000);
   } break;
   case KK_F1:
   {
      return (bool)(GetAsyncKeyState( VK_F1 ) & 0x8000);
   } break;
   case KK_F2:
   {
      return (bool)(GetAsyncKeyState( VK_F2 ) & 0x8000);
   } break;
   case KK_F3:
   {
      return (bool)(GetAsyncKeyState( VK_F3 ) & 0x8000);
   } break;
   case KK_F4:
   {
      return (bool)(GetAsyncKeyState( VK_F4 ) & 0x8000);
   } break;
   case KK_F5:
   {
      return (bool)(GetAsyncKeyState( VK_F5 ) & 0x8000);
   } break;
   case KK_F6:
   {
      return (bool)(GetAsyncKeyState( VK_F6 ) & 0x8000);
   } break;
   case KK_F7:
   {
      return (bool)(GetAsyncKeyState( VK_F7 ) & 0x8000);
   } break;
   case KK_F8:
   {
      return (bool)(GetAsyncKeyState( VK_F8 ) & 0x8000);
   } break;
   case KK_F9:
   {
      return (bool)(GetAsyncKeyState( VK_F9 ) & 0x8000);
   } break;
   case KK_F10:
   {
      return (bool)(GetAsyncKeyState( VK_F10 ) & 0x8000);
   } break;
   case KK_F11:
   {
      return (bool)(GetAsyncKeyState( VK_F11 ) & 0x8000);
   } break;
   case KK_F12:
   {
      return (bool)(GetAsyncKeyState( VK_F12 ) & 0x8000);
   } break;
   case KK_Tilde:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_3 ) & 0x8000);
   } break;
   case KK_0:
   {
      return (bool)(GetAsyncKeyState( '0' ) & 0x8000);
   } break;
   case KK_1:
   {
      return (bool)(GetAsyncKeyState( '1' ) & 0x8000);
   } break;
   case KK_2:
   {
      return (bool)(GetAsyncKeyState( '2' ) & 0x8000);
   } break;
   case KK_3:
   {
      return (bool)(GetAsyncKeyState( '3' ) & 0x8000);
   } break;
   case KK_4:
   {
      return (bool)(GetAsyncKeyState( '4' ) & 0x8000);
   } break;
   case KK_5:
   {
      return (bool)(GetAsyncKeyState( '5' ) & 0x8000);
   } break;
   case KK_6:
   {
      return (bool)(GetAsyncKeyState( '6' ) & 0x8000);
   } break;
   case KK_7:
   {
      return (bool)(GetAsyncKeyState( '7' ) & 0x8000);
   } break;
   case KK_8:
   {
      return (bool)(GetAsyncKeyState( '8' ) & 0x8000);
   } break;
   case KK_9:
   {
      return (bool)(GetAsyncKeyState( '9' ) & 0x8000);
   } break;
   case KK_Dash:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_MINUS ) & 0x8000);
   } break;
   case KK_Equals:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_PLUS ) & 0x8000);
   } break;
   case KK_BackSpace:
   {
      return (bool)(GetAsyncKeyState( VK_BACK ) & 0x8000);
   } break;
   case KK_A:
   {
      return (bool)(GetAsyncKeyState( 'A' ) & 0x8000);
   } break;
   case KK_B:
   {
      return (bool)(GetAsyncKeyState( 'B' ) & 0x8000);
   } break;
   case KK_C:
   {
      return (bool)(GetAsyncKeyState( 'C' ) & 0x8000);
   } break;
   case KK_D:
   {
      return (bool)(GetAsyncKeyState( 'D' ) & 0x8000);
   } break;
   case KK_E:
   {
      return (bool)(GetAsyncKeyState( 'E' ) & 0x8000);
   } break;
   case KK_F:
   {
      return (bool)(GetAsyncKeyState( 'F' ) & 0x8000);
   } break;
   case KK_G:
   {
      return (bool)(GetAsyncKeyState( 'G' ) & 0x8000);
   } break;
   case KK_H:
   {
      return (bool)(GetAsyncKeyState( 'H' ) & 0x8000);
   } break;
   case KK_I:
   {
      return (bool)(GetAsyncKeyState( 'I' ) & 0x8000);
   } break;
   case KK_J:
   {
      return (bool)(GetAsyncKeyState( 'J' ) & 0x8000);
   } break;
   case KK_K:
   {
      return (bool)(GetAsyncKeyState( 'K' ) & 0x8000);
   } break;
   case KK_L:
   {
      return (bool)(GetAsyncKeyState( 'L' ) & 0x8000);
   } break;
   case KK_M:
   {
      return (bool)(GetAsyncKeyState( 'M' ) & 0x8000);
   } break;
   case KK_N:
   {
      return (bool)(GetAsyncKeyState( 'N' ) & 0x8000);
   } break;
   case KK_O:
   {
      return (bool)(GetAsyncKeyState( 'O' ) & 0x8000);
   } break;
   case KK_P:
   {
      return (bool)(GetAsyncKeyState( 'P' ) & 0x8000);
   } break;
   case KK_Q:
   {
      return (bool)(GetAsyncKeyState( 'Q' ) & 0x8000);
   } break;
   case KK_R:
   {
      return (bool)(GetAsyncKeyState( 'R' ) & 0x8000);
   } break;
   case KK_S:
   {
      return (bool)(GetAsyncKeyState( 'S' ) & 0x8000);
   } break;
   case KK_T:
   {
      return (bool)(GetAsyncKeyState( 'T' ) & 0x8000);
   } break;
   case KK_U:
   {
      return (bool)(GetAsyncKeyState( 'U' ) & 0x8000);
   } break;
   case KK_V:
   {
      return (bool)(GetAsyncKeyState( 'V' ) & 0x8000);
   } break;
   case KK_W:
   {
      return (bool)(GetAsyncKeyState( 'W' ) & 0x8000);
   } break;
   case KK_X:
   {
      return (bool)(GetAsyncKeyState( 'X' ) & 0x8000);
   } break;
   case KK_Y:
   {
      return (bool)(GetAsyncKeyState( 'Y' ) & 0x8000);
   } break;
   case KK_Z:
   {
      return (bool)(GetAsyncKeyState( 'Z' ) & 0x8000);
   } break;
   case KK_Tab:
   {
      return (bool)(GetAsyncKeyState( VK_TAB ) & 0x8000);
   } break;
   case KK_LeftBracket:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_4 ) & 0x8000);
   } break;
   case KK_RightBracket:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_6 ) & 0x8000);
   } break;
   case KK_BackSlash:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_5 ) & 0x8000);
   } break;
   case KK_CapsLock:
   {
      return (bool)(GetAsyncKeyState( VK_CAPITAL ) & 0x8000);
   } break;
   case KK_Colon:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_1 ) & 0x8000);
   } break;
   case KK_Quote:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_7 ) & 0x8000);
   } break;
   case KK_Return:
   {
      return (bool)(GetAsyncKeyState( VK_RETURN ) & 0x8000);
   } break;
   case KK_LeftShift:
   {
      return (bool)(GetAsyncKeyState( VK_LSHIFT ) & 0x8000);
   } break;
   case KK_Comma:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_COMMA ) & 0x8000);
   } break;
   case KK_Period:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_PERIOD ) & 0x8000);
   } break;
   case KK_Slash:
   {
      return (bool)(GetAsyncKeyState( VK_OEM_2 ) & 0x8000);
   } break;
   case KK_RightShift:
   {
      return (bool)(GetAsyncKeyState( VK_RSHIFT ) & 0x8000);
   } break;
   case KK_LeftCtrl:
   {
      return (bool)(GetAsyncKeyState( VK_LCONTROL ) & 0x8000);
   } break;
   case KK_LeftWindows:
   {
      return (bool)(GetAsyncKeyState( VK_LWIN ) & 0x8000);
   } break;
   case KK_LeftAlt:
   {
      return (bool)(GetAsyncKeyState( VK_LMENU ) & 0x8000);
   } break;
   case KK_Space:
   {
      return (bool)(GetAsyncKeyState( VK_SPACE ) & 0x8000);
   } break;
   case KK_RightAlt:
   {
      return (bool)(GetAsyncKeyState( VK_RMENU ) & 0x8000);
   } break;
   case KK_RightWindows:
   {
      return (bool)(GetAsyncKeyState( VK_RWIN ) & 0x8000);
   } break;
   case KK_ContextMenu:
   {
      return (bool)(GetAsyncKeyState( VK_APPS ) & 0x8000);
   } break;
   case KK_RightCtrl:
   {
      return (bool)(GetAsyncKeyState( VK_RCONTROL ) & 0x8000);
   } break;

   // Cursor keys column of the keyboard
   case KK_PrintScreen:
   {
      return (bool)(GetAsyncKeyState( VK_SNAPSHOT ) & 0x8000);
   } break;
   case KK_ScrollLock:
   {
      return (bool)(GetAsyncKeyState( VK_SCROLL ) & 0x8000);
   } break;
   case KK_PauseBreak:
   {
      return (bool)(GetAsyncKeyState( VK_PAUSE ) & 0x8000);
   } break;
   case KK_Insert:
   {
      return (bool)(GetAsyncKeyState( VK_INSERT ) & 0x8000);
   } break;
   case KK_Home:
   {
      return (bool)(GetAsyncKeyState( VK_HOME ) & 0x8000);
   } break;
   case KK_PageUp:
   {
      return (bool)(GetAsyncKeyState( VK_PRIOR ) & 0x8000);
   } break;
   case KK_Delete:
   {
      return (bool)(GetAsyncKeyState( VK_DELETE ) & 0x8000);
   } break;
   case KK_End:
   {
      return (bool)(GetAsyncKeyState( VK_END ) & 0x8000);
   } break;
   case KK_PageDown:
   {
      return (bool)(GetAsyncKeyState( VK_NEXT ) & 0x8000);
   } break;
   case KK_Up:
   {
      return (bool)(GetAsyncKeyState( VK_UP ) & 0x8000);
   } break;
   case KK_Left:
   {
      return (bool)(GetAsyncKeyState( VK_LEFT ) & 0x8000);
   } break;
   case KK_Down:
   {
      return (bool)(GetAsyncKeyState( VK_DOWN ) & 0x8000);
   } break;
   case KK_Right:
   {
      return (bool)(GetAsyncKeyState( VK_RIGHT ) & 0x8000);
   } break;

   // Numeric Keypad column of the keyboard
   case KK_NumLock:
   {
      return (bool)(GetAsyncKeyState( VK_NUMLOCK ) & 0x8000);
   } break;
   case KK_NumForwardSlash:
   {
      return (bool)(GetAsyncKeyState( VK_DIVIDE ) & 0x8000);
   } break;
   case KK_Asterisk:
   {
      return (bool)(GetAsyncKeyState( VK_MULTIPLY ) & 0x8000);
   } break;
   case KK_Minus:
   {
      return (bool)(GetAsyncKeyState( VK_SUBTRACT ) & 0x8000);
   } break;
   case KK_Num0:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD0 ) & 0x8000);
   } break;
   case KK_Num1:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD1 ) & 0x8000);
   } break;
   case KK_Num2:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD2 ) & 0x8000);
   } break;
   case KK_Num3:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD3 ) & 0x8000);
   } break;
   case KK_Num4:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD4 ) & 0x8000);
   } break;
   case KK_Num5:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD5 ) & 0x8000);
   } break;
   case KK_Num6:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD6 ) & 0x8000);
   } break;
   case KK_Num7:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD7 ) & 0x8000);
   } break;
   case KK_Num8:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD8 ) & 0x8000);
   } break;
   case KK_Num9:
   {
      return (bool)(GetAsyncKeyState( VK_NUMPAD9 ) & 0x8000);
   } break;
   case KK_NumPlus:
   {
      return (bool)(GetAsyncKeyState( VK_ADD ) & 0x8000);
   } break;
   case KK_NumPeriod:
   {
      return (bool)(GetAsyncKeyState( VK_DECIMAL ) & 0x8000);
   } break;
   case KK_NumEnter:
   {
      Assert( false, "Numpad enter can't be queried on windows, sorry." );
      return false;//(bool)(GetAsyncKeyState(  ) & 0x8000);
   } break;
   default:
      Assert( false, "Key not found in OperatingSystem::IsKeyboardKeyDown." );
      return false;
   }
}

void OperatingSystem::SystemUpdate( double )
{
}

void OperatingSystem::SystemClose()
{
}

// OS Commands
eMessageResults OperatingSystem::CreateOSDialog( const std::string & Title, const std::string & Message,
                                                 const eMessageTypes Type, const eMessageOptions Options,
                                                 tWindowID WindowID )
{
   UINT OSMessageType = MB_ICONINFORMATION;

   if ( Type == kWarningMessage )
   {
      OSMessageType = MB_ICONWARNING;
   }

   switch ( Options )
   {
      case kConfirm:
         OSMessageType |= MB_OK;
         break;

      case kConfirmCancel:
         OSMessageType |= MB_OKCANCEL;
         break;

      case kYesNo:
         OSMessageType |= MB_YESNO;
         break;

      case kYesNoCancel:
         OSMessageType |= MB_YESNOCANCEL;
         break;

      case kRetryCancel:
         OSMessageType |= MB_RETRYCANCEL;
         break;

      case kCancelRetryContinue:
         OSMessageType |= MB_CANCELTRYCONTINUE;
         break;

      default:
         OSMessageType |= MB_OK;
         break;
   }

   HWND WindowHandle = NULL;
   ocWindowsWindow * ActiveWindow = NULL;
   if ( WindowID != kNull8 )
   {
      ocWindowsWindow * Window = osData->WindowIDsToWindows[WindowID];
      WindowHandle = Window->GetHandle();
   }
   else
   {
      ActiveWindow = GetActiveContextWindow( osData );
      if ( ActiveWindow != NULL )
      {
         WindowHandle = ActiveWindow->GetHandle();
      }
   }
   UINT OSMessageResult = MessageBox( WindowHandle, Message.c_str(), Title.c_str(), OSMessageType );

   switch ( OSMessageResult )
   {
      case IDOK:
         return kMessageConfirm;

      case IDYES:
         return kMessageYes;

      case IDNO:
         return kMessageNo;

      case IDCANCEL:
         // Fall through
      case IDABORT:
         return kMessageCancel;

      case IDCONTINUE:
         // Fall through
      case IDIGNORE:
         return kMessageContinue;

      case IDTRYAGAIN:
         // Fall through
      case IDRETRY:
         return kMessageRetry;

      default:
         return kMessageConfirm;
   }
}

uint32_t OperatingSystem::AddConcreteComponent( ocWindow * NewWindow )
{
    return AddComponent( NewWindow );
}
